package org.elsfs.cloud.common.mybatis.injector.methods;


import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.elsfs.cloud.common.mybatis.ext.mybatis.extension.JoinWrapper;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * 抽象连接方法实现
 *

 */
public abstract class AbstractJoinMethod extends AbstractMethod {
    public static final String LOGIC_DELETE_FIELD_NAME = "deleteFlag";

    /**
     * 逻辑删除字段名称
     */
    private static final String JOIN_LOGIC_FIELD_NAME = JoinWrapper.TABLE_ALIAS + "." + LOGIC_DELETE_FIELD_NAME;
    /**
     * SQL脚本
     */
    private static final String SQL_SCRIPT = "<script>%s SELECT %s FROM %s %s %s %s\n</script>";
    /**
     * 获取JOIN SQL方法
     */
    protected static final String JOIN_SQL_METHOD = WRAPPER_DOT + "getJoinSqlSegment()";

    /**
     * @param methodName 方法名
     * @since 3.5.0
     */
    protected AbstractJoinMethod(String methodName) {
        super(methodName);
    }

    /**
     * SQL 查询所有表字段
     *
     * @param table        表信息
     * @param queryWrapper 是否为使用 queryWrapper 查询
     * @return sql 脚本
     */
    @Override
    protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) {
        /* 假设存在用户自定义的 resultMap 映射返回 */
        String selectColumns = ASTERISK;
        if (table.getResultMap() == null || table.isAutoInitResultMap()) {
            /* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */

            selectColumns =  Arrays.stream(StringUtils.split(table.getAllSqlSelect(),","))
                .map(field -> JoinWrapper.TABLE_ALIAS_PREFIX + field)
                .collect(Collectors.joining(JoinWrapper.FIELD_SEPARATOR));
        }

        if (!queryWrapper) {
            return selectColumns;
        }

        String unSafeParam = SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT);
        return SqlScriptUtils.convertChoose(String.format("%s != null and %s != null", WRAPPER, Q_WRAPPER_SQL_SELECT),
            SqlScriptUtils.convertChoose(String.format("%s", WRAPPER_DOT + "defaultSelectedFieldsCleared"), unSafeParam, selectColumns + JoinWrapper.FIELD_SEPARATOR + unSafeParam),
            selectColumns);
    }

    /**
     * EntityWrapper方式获取select where
     *
     * @param newLine 是否提到下一行
     * @param table   表信息
     * @return String
     */
    @Override
    protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
        if (table.isWithLogicDelete()) {
            StringBuilder sqlScript = new StringBuilder(
                SqlScriptUtils.convertIf(table.getAllSqlWhere(true, true,true, WRAPPER_ENTITY_DOT), String.format("%s != null", WRAPPER_ENTITY),
                    true)
            );
            sqlScript.append(NEWLINE)
                .append(this.handleLogicDeleteSql(table.getLogicDeleteSql(true, true)))
                .append(NEWLINE);
            String normalSqlScript = SqlScriptUtils.convertIf(String.format("AND ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                    WRAPPER_NONEMPTYOFNORMAL), true) + NEWLINE
                + SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                    WRAPPER_EMPTYOFNORMAL), true);
            sqlScript.append(normalSqlScript);
            String sql = SqlScriptUtils.convertWhere(
                SqlScriptUtils.convertChoose(String.format("%s != null", WRAPPER), sqlScript.toString(),
                    this.handleLogicDeleteSql(table.getLogicDeleteSql(false, true)))
            );
            return newLine ? NEWLINE + sql : sql;
        } else {
            StringBuilder sqlScript = new StringBuilder(
                SqlScriptUtils.convertIf(table.getAllSqlWhere(false, true,true, WRAPPER_ENTITY_DOT), String.format("%s != null", WRAPPER_ENTITY), true)
            );
            sqlScript.append(NEWLINE)
                .append(
                    SqlScriptUtils.convertIf(String.format(SqlScriptUtils.convertIf(" AND", String.format("%s and %s", WRAPPER_NONEMPTYOFENTITY, WRAPPER_NONEMPTYOFNORMAL), false) + " ${%s}", WRAPPER_SQLSEGMENT),
                        String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_NONEMPTYOFWHERE), true)
                );

            sqlScript = new StringBuilder(
                SqlScriptUtils.convertWhere(sqlScript.toString())
            );
            sqlScript.append(NEWLINE)
                .append(
                    SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
                        String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
                            WRAPPER_EMPTYOFWHERE), true)
                );
            String sql = SqlScriptUtils.convertIf(sqlScript.toString(), String.format("%s != null", WRAPPER), true);
            return newLine ? NEWLINE + sql : sql;
        }
    }

    /**
     * 替换连表SQL中的逻辑删除字段名称
     *
     * @param sql SQL
     * @return 替换后的SQL
     */
    protected String handleLogicDeleteSql(String sql) {
        return sql.replace( LOGIC_DELETE_FIELD_NAME, JOIN_LOGIC_FIELD_NAME);
    }

    /**
     * 注入自定义 MappedStatement
     *
     * @param mapperClass mapper 接口
     * @param modelClass  mapper 泛型
     * @param tableInfo   数据库表反射信息
     * @return MappedStatement
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        String sql = String.format(
            SQL_SCRIPT,
            this.sqlFirst(),
            this.sqlSelectColumns(tableInfo, true),
            tableInfo.getTableName() + " AS " + JoinWrapper.TABLE_ALIAS,
            SqlScriptUtils.unSafeParam(JOIN_SQL_METHOD),
            this.sqlWhereEntityWrapper(true, tableInfo),
            this.sqlComment()
        );

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, tableInfo);
    }
}
